/// <reference path="./asset.ts" />

namespace FIREFLYX {
    /** accessor 的 ComponentType*/
    enum AccComponentTEnum {
        BYTE = 5120,
        UNSIGNED_BYTE = 5121,
        SHORT = 5122,
        UNSIGNED_SHORT = 5123,
        UNSIGNED_INT = 5125,
        FLOAT = 5126,
    }

    /** MeshPrimitives 模式  */
    enum MeshPrimitivesMode {
        POINTS,
        LINES,
        LINE_LOOP,
        LINE_STRIP,
        TRIANGLES,
        TRIANGLE_STRIP,
        TRIANGLE_FAN,
    }

    type AccComponentType = Uint8Array | Int8Array | Uint16Array | Int16Array | Uint32Array | Float32Array;
    type AccComponentTypeC = Int8ArrayConstructor | Uint8ArrayConstructor | Int16ArrayConstructor | Uint16ArrayConstructor | Uint32ArrayConstructor | Float32ArrayConstructor;
    type Attribute = { [attrKey: string]: number };
    /** GLTF 数据 的 accessors.Sparse 项 */
    type GLTFAccessorSparse = {
        count: number,
        indices: { bufferView: number, byteOffset?: number, componentType: number },
        values: { bufferView: number, byteOffset?: number },
    };
    /** GLTF 数据 的 accessors 项 */
    type GLTFAccessor = {
        bufferView?: number,
        byteOffset?: number,
        componentType: AccComponentTEnum,
        normalized?: boolean,
        count: number,
        type: string,
        max?: number[],
        min?: number[],
        sparse?: GLTFAccessorSparse,
        name?: string,
    };
    /** GLTF 数据 的 buffers 项 */
    type GLTFBuffers = {
        uri?: string,
        byteLength: number,
        name: number
    };
    /** GLTF 数据 的 bufferViews 项 */
    type GLTFBufferView = {
        buffer: number,
        byteOffset?: number,
        byteLength: number,
        byteStride?: number,
        target?: number,
        name?: string,
    };
    /** GLTF 数据 的 images 项 */
    type GLTFImage = {
        uri?: string,
        mimeType?: string,
        bufferView?: number,
        name?: string,
    };
    /** GLTF 数据 的 mesh 项 */
    type GLTFMeshPrimitives = {
        attributes: Attribute,
        indices?: number,
        material?: number,
        mode?: MeshPrimitivesMode,
        targets?: Attribute[],
    };
    /** GLTF 数据 的 mesh 项 */
    type GLTFMesh = {
        primitives: GLTFMeshPrimitives[],
        weights?: number[],
        name?: string,
    };
    /** GLTF 数据 的 nodes 项 */
    type GLTFNode = {
        name: string
        children?: number[],
        mesh?: number,
        matrix?: number[],
        scale?: number[],
        rotation?: number[],
        translation?: number[],
        skin?: any,
        camera?: any,
    };
    /** GLTF 数据 的 samplers 项 */
    type GLTFSampler = {
        magFilter?: number;
        minFilter?: number;
        wrapS?: number;
        wrapT?: number;
    };
    /** GLTF 数据 的 scenes 项 */
    type GLTFScene = {
        name: string,
        nodes?: number[];
    };
    /** GLTF 数据 的 textures 项 */
    type GLTFTexture = {
        source: number,
        sampler?: number,
        name?: string,
    };
    /** GLTF 数据 的 TextureInfo 项 */
    type GLTFTextureInfo = {
        index: number;
        texCoord?: number;
    }
    /** GLTF 数据 的 NormalTextureInfo 项 */
    type GLTFNormalTextureInfo = {
        index: number;
        texCoord?: number;
        scale?: number;
    }
    /** GLTF 数据 的 OcclusionTextureInfo 项 */
    type GLTFOcclusionTextureInfo = {
        index: number;
        texCoord?: number;
        strength?: number;
    }
    /** GLTF 数据 的 PBRMetallicRoughness 项 */
    type GLTFPBRMetallicRoughness = {
        baseColorFactor?: number[];
        baseColorTexture?: GLTFTextureInfo;
        metallicFactor?: number;
        roughnessFactor?: number;
        metallicRoughnessTexture?: GLTFTextureInfo;
    }
    /** GLTF 数据 的 Material 项  */
    type GLTFMaterial = {
        name?: string;
        pbrMetallicRoughness?: GLTFPBRMetallicRoughness;
        normalTexture?: GLTFNormalTextureInfo;
        occlusionTexture?: GLTFOcclusionTextureInfo;
        emissiveTexture?: GLTFTextureInfo;
        /**发射颜色,默认值[0,0,0]*/
        emissiveFactor?: number[];
        alphaMode?: string;
        alphaCutoff?: number;
        doubleSided?: boolean;
    };
    /** GLTF 数据 */
    type GLTFData = {
        extensionsUsed: string[],
        extensionsRequired: string[],
        accessors: GLTFAccessor[],
        asset: { version: string, generator?: string, copyright?: number },
        buffers: GLTFBuffers[],
        bufferViews: GLTFBufferView[],
        images: GLTFImage[],
        materials: GLTFMaterial[],
        meshes: GLTFMesh[],
        nodes: GLTFNode[],
        samplers: GLTFSampler[],
        scene: number,
        scenes: GLTFScene[],
        textures: GLTFTexture[],
    };

    /** GLTF 资源 */
    export class GLTF extends Asset {
        public static readonly BIN_TAG = "_GLTF_BIN_";
        private static readonly MESH_TAG = "_GLTF_MESH_";
        private static readonly IMAGE_TAG = "_GLTF_IMAGE_";
        private static readonly MATERIAL_TAG = "_GLTF_MATERIAL_";
        private _data: GLTFData;
        private _isLoadAll: boolean = false;
        private _isParseAll: boolean = false;
        private _gltfPath: string = "";
        private _accInfoCacheMap: { [i: number]: { bufferIdx: number, compType: number, byteOffset: number, byteLen: number } } = {};
        private _binResKeys: string[] = [];
        private _meshResKeys: string[] = [];
        private _imageResKeys: string[] = [];
        private _materialResKeys: string[] = [];
        private _accIdMarkMap: Map<number, boolean> = new Map();

        /** 设置数据 */
        public setData(data: GLTFData, gltfPath: string) {
            this._data = data;
            this._gltfPath = gltfPath;
            // tslint:disable-next-line: no-unused-expression
            this._isLoadAll = false;
            this._isParseAll = false;
        }

        /** 是否依赖资源全部加载完毕 */
        public isLoadedAll(): boolean {
            return this._isLoadAll;
        }

        /** 是否依赖资源全部解析完毕 */
        public isParseAll(): boolean {
            return this._isParseAll;
        }

        /** 加载所有依赖的资源 */
        public async loadAllFile() {
            if (this._isLoadAll) return;
            if (!this._data) {
                console.error(`can't to load ,_data is null.`);
                return;
            }
            //加载所有文件
            const binFiles: Promise<any>[] = [];

            //二进制数据
            const buffers = this._data.buffers;
            if (buffers) {
                buffers.forEach((buf, idx) => {
                    let bufPath = buf.uri ? buf.uri : `${GLTF.BIN_TAG}${idx}`;
                    const fUrl = `${this._gltfPath}${bufPath}`;
                    this._binResKeys.push(fUrl);
                    if (buf.uri) {  //有 uri 需要加载 或 base64解析？
                        if (!AssetManager.GetAsset(fUrl)) { binFiles.push(AssetManager.LoadFile(fUrl)); }
                    }
                });
            }
            //等待加载 buffer bin 文件
            if (binFiles.length > 0) {
                await Promise.all(binFiles);
            }

            //图
            const images = this._data.images;
            if (images) {
                const imgFile: Promise<any>[] = [];

                images.forEach((i, index) => {
                    let resPath = "";
                    if (i.uri) {
                        resPath = `${this._gltfPath}${i.uri}`;
                        if (!AssetManager.GetAsset(resPath)) { imgFile.push(AssetManager.LoadFile(resPath)); }
                        ResPathProcessorParameterMap.set(resPath, { flipY: false });    //import texture时不要 Y 翻转
                        //uri 是否是base64 信息
                    } else if (i.bufferView != null) {
                        let imgName = i.name ?? `${GLTF.IMAGE_TAG}${index}`;
                        resPath = `${this._gltfPath}${imgName}`;
                        imgFile.push(this.getImageByBin(index));
                    }

                    this._imageResKeys.push(resPath);
                });

                if (imgFile.length > 0) {
                    await Promise.all(imgFile);
                }
            }

            //等待所有载完
            this._isLoadAll = true;
        }

        /** 解析所有资源，纹理、材质、网格、动画... */
        public async parseAllRes() {
            if (this._isParseAll) return;
            //当前对象不持有具体的资源对象，只持有key ，具体对象方到 AssetManager.CacheResMap中
            const _data = this._data;
            //解析纹理
            if (_data.textures) {
                for (let i = 0, len = _data.textures.length; i < len; i++) {
                    const tex = this.getTexture(i);
                    if (!tex) continue;
                }
            }

            //解析材质
            for (let i = 0, len = _data.materials.length; i < len; i++) {
                const material = this.getMaterial(i);
                if (!material) continue;
                const rKey = `${this._gltfPath}${GLTF.MATERIAL_TAG}${material?.name}`;
                this._materialResKeys.push(rKey);
                AssetManager.SetAsset(rKey, material);
            }

            //解析网格
            for (let i = 0, len = _data.meshes.length; i < len; i++) {
                const rM = this.getMesh(i);
                if (!rM) continue;
                const rKey = `${this._gltfPath}${GLTF.MESH_TAG}${rM?.name}`;
                this._meshResKeys.push(rKey);
                AssetManager.SetAsset(rKey, rM);
            }

            //解析动画

            //等待所有载完
            this._isParseAll = true;
        }

        /** 获取节点对象 */
        public getGameObject(): Nullable<GameObject> {
            const d = this._data;
            const s = d.scenes[d.scene];
            const nodes = s.nodes;
            if (!nodes) return null;
            //模型跟节点
            const gltfGameObject = new GameObject(s.name);
            const nodeRoot = gltfGameObject.transform;
            //子节点
            for (let i = 0, len = nodes.length; i < len; i++) {
                const nIdx = nodes[i];
                const n = this.getNode(nIdx);
                if (n) { n.transform.parent = nodeRoot; }
            }
            return gltfGameObject;
        }

        /** 获取节点对象 （异步） */
        public async getGameObjectAsync(): Promise<Nullable<GameObject>> {
            if (!this._isLoadAll) {
                await this.loadAllFile();
            }

            if (!this._isParseAll) {
                await this.parseAllRes();
            }

            return this.getGameObject();
        }

        /**
         * 获取指定索引的节点对象
         * @param index 索引值
         * @returns 
         */
        private getNode(index: number): Nullable<GameObject> {
            const node = this._data.nodes[index];
            const n = new GameObject(node.name);
            const trans = n.transform;
            //设置transform
            const matrix = node.matrix;
            if (!matrix) {
                //位置、旋转、缩放 数据
                const translation = node.translation;
                if (translation) {
                    //修正 x ,左右手坐标系不一致问题 
                    trans.localPostion.set(-1 * translation[0], translation[1], translation[2]);
                    trans.localPostion = trans.localPostion;
                }
                const rotation = node.rotation;
                if (rotation) {
                    trans.localRotation.set(rotation[0], rotation[1], rotation[2], rotation[3]);
                    trans.localRotation = trans.localRotation;
                }
                const scale = node.scale;
                if (scale) {
                    trans.localScale.set(scale[0], scale[1], scale[2]);
                    trans.localScale = trans.localScale;
                }
            } else {
                //矩阵数据
                const lMtx = trans.getLocalMatrix();
                for (let i = 0; i < 16; i++) {
                    lMtx.m[i] = matrix[i];
                }

                lMtx.m[12] *= -1;   //修正 x ,左右手坐标系不一致问题 

                lMtx.decompose(trans.localScale, trans.localRotation, trans.localPostion);
                trans.localPostion = trans.localPostion;
                trans.localRotation = trans.localRotation;
                trans.localScale = trans.localScale;
            }

            //静态网格
            const meshIdx = node.mesh;
            let m: Nullable<Mesh> = null;
            if (meshIdx != null) {
                const rKey = this._meshResKeys[meshIdx];
                m = AssetManager.GetAsset(rKey) as Nullable<Mesh>;
            }

            //蒙皮骨骼
            const isSkinMesh = false;

            //相机

            //组件拼装
            if (m) {
                const mesh = this._data.meshes[meshIdx as number];

                if (!isSkinMesh) {
                    const mr = n.addComponent(MeshRenderer) as MeshRenderer;
                    mr.mesh = m;
                    //添加材质
                    const primitves = mesh.primitives;
                    if (primitves) {
                        //每个primitves 对应一组材质
                        for (let i = 0, len = primitves.length; i < len; i++) {
                            const primitve = primitves[i];
                            if (primitve.material == null) continue;
                            // const material = this._data.materials[primitve.material];
                            //加材质
                            // const mat = new Material(`DEF_MODEL`);
                            const resMatKey = this._materialResKeys[primitve.material];
                            const mat = AssetManager.GetAsset(resMatKey) as Material;
                            if (!mat) continue;
                            mr.materialGroupList[0][i] = mat;
                        }
                    }
                }
            }

            //子节点
            const children = node.children;
            if (children) {
                for (let i = 0, len = children.length; i < len; i++) {
                    const cN = this.getNode(children[i]);
                    if (!cN) continue;
                    cN.transform.parent = trans;
                }
            }
            return n;
        }

        /**
         * 获取 image 通过 二进制数据
         * @param imageIdx image 索引
         * @returns 
         */
        private async getImageByBin(imageIdx: number) {
            let image = this._data.images[imageIdx];
            if (image.bufferView == null) return;
            let bufView = this._data.bufferViews[image.bufferView];
            let binResKey = this._binResKeys[bufView.buffer];
            let bin = AssetManager.GetAsset(binResKey) as BinaryData;
            if (!bin) return;
            let imgBin = new Uint8Array(bin.buffer, bufView.byteOffset ?? 0, bufView.byteLength);

            const blob = new Blob([imgBin], {
                type: image.mimeType
            });
            let sourceURI = URL.createObjectURL(blob);
            let img = await FileLoad.loadImage(sourceURI);
            //纹理储存设置
            let imgName = image.name ?? `${GLTF.IMAGE_TAG}${imageIdx}`;
            let tex: Texture2D = new Texture2D();
            tex.paramSetting.flipY = false;
            tex.setTextureData(img);
            //应用参数
            tex.apply();
            //保存到 AssetManager
            let resPath = `${this._gltfPath}${imgName}`;
            AssetManager.SetAsset(resPath, tex);
        }

        /**
         * 获取指定索引的纹理对象
         * @param index 索引值
         * @returns 
         */
        private getTexture(index: number): Nullable<Texture2D> {
            const d = this._data;
            const texture = d.textures[index];
            const gl = App.webgl2;
            const resKeyImage = this._imageResKeys[texture.source];
            const resTexture = AssetManager.GetAsset(resKeyImage) as Texture2D;
            if (!resTexture) return null;
            //sampler
            let magFilter: number = gl.LINEAR;
            let minFilter: number = gl.LINEAR;
            let wrapS: number = gl.REPEAT;
            let wrapT: number = gl.REPEAT;
            if (texture.sampler != null && d.samplers) {
                const sampler = d.samplers[texture.sampler];
                magFilter = sampler.magFilter ?? magFilter;
                minFilter = sampler.minFilter ?? minFilter;
                wrapS = sampler.wrapS ?? wrapS;
                wrapT = sampler.wrapT ?? wrapT;
            }

            //设置参数
            const para = resTexture.paramSetting;
            para.linear = magFilter == gl.LINEAR || (minFilter == gl.LINEAR_MIPMAP_LINEAR || minFilter == gl.LINEAR_MIPMAP_NEAREST || minFilter == gl.LINEAR) ? true : false;
            para.mipmap = minFilter == gl.LINEAR_MIPMAP_LINEAR ||
                minFilter == gl.LINEAR_MIPMAP_NEAREST ||
                minFilter == gl.NEAREST_MIPMAP_LINEAR ||
                minFilter == gl.NEAREST_MIPMAP_NEAREST
                ? true : false;
            para.warpU = wrapS;
            para.warpV = wrapT;
            //应用纹理参数
            resTexture.apply();
            return null;
        }

        /**
         * 获取指定索引的渲染材质对象
         * @param index 
         * @returns 
         */
        private getMaterial(index: number): Nullable<Material> {
            const d = this._data;
            const material = d.materials[index];
            const mName = material.name ?? `Material_${index}`;
            if (!material) return null;
            const resMaterial = new Material(mName);
            //doubleSided
            resMaterial.rasterState = RasterState.getState(material.doubleSided ? CullMode.NONE : CullMode.BACK);
            //暂时 用普通shader 
            // resMaterial.shader = DefaultAsset.GetShader(FIREFLYX.BUILTIN_SHADER_BUMPED_SPECULAR);
            resMaterial.shader = DefaultAsset.GetShader(FIREFLYX.BUILTIN_SHADER_STANDARD);
            const pbrMetallicRoughness = material.pbrMetallicRoughness;
            //PBR 金属度 + 粗糙度
            if (pbrMetallicRoughness) {
                //主纹理
                const mainTexRKey = this._imageResKeys[pbrMetallicRoughness.baseColorTexture?.index ?? -1];
                const resTex = AssetManager.GetAsset(mainTexRKey) as Texture2D;
                resMaterial.setProperty(`u_mainTex`, resTex);
                //金属粗糙纹理
                const metRouRKey = this._imageResKeys[pbrMetallicRoughness.metallicRoughnessTexture?.index ?? -1];
                const resMRTex = AssetManager.GetAsset(metRouRKey) as Texture2D;
                resMaterial.setProperty(`u_metaRougTex`, resMRTex);
            }

            //alphaMode 半透模式
            if (material.alphaMode) {
                //默认 ： "OPAQUE"
            }

            //alphaCutoff alpha剔除
            if (material.alphaCutoff) {
                //默认 ： 0.5
            }

            //双面绘制
            if (material.doubleSided) {
                //默认 ： false
            }

            //法线纹理
            if (material.normalTexture) {
                const normalTexRKey = this._imageResKeys[material.normalTexture?.index ?? -1];
                const resNTex = AssetManager.GetAsset(normalTexRKey) as Texture2D;
                resMaterial.setProperty(`u_normalTex`, resNTex);
                //法线纹理缩放
                const nScale = material.normalTexture?.scale ?? 1;
                resMaterial.setProperty(`u_normalScale`, nScale);
            }

            //自发光纹理
            if (material.emissiveTexture) {
                const emissiveTexRKey = this._imageResKeys[material.emissiveTexture?.index ?? -1];
                const resETex = AssetManager.GetAsset(emissiveTexRKey) as Texture2D;
                resMaterial.setProperty(`u_emissiveTex`, resETex);
            }

            //遮蔽纹理
            if (material.occlusionTexture) {
                const occlusionTexRKey = this._imageResKeys[material.occlusionTexture?.index ?? -1];
                const resOTex = AssetManager.GetAsset(occlusionTexRKey) as Texture2D;
                resMaterial.setProperty(`u_occlusionTex`, resOTex);
                //遮蔽 强度
                const oStrength = material.occlusionTexture?.strength ?? 1;
                resMaterial.setProperty(`u_emissiveStrength`, oStrength);
            }

            //自发光 颜色系数
            if (material.emissiveFactor) {
                resMaterial.setProperty(`u_emissiveFactor`, material.emissiveFactor);
            }

            return resMaterial;
        }

        /**
         * x 翻转
         * 处理左右手坐标系不一致问题
         * @param accIdx 
         * @param arr 
         */
        private reverseVec3X(accIdx: number, arr: AccComponentType) {
            if (!this._accIdMarkMap.get(accIdx)) {
                this._accIdMarkMap.set(accIdx, true);
                //修正坐标系
                for (let k = 0, len2 = arr.length; k < len2; k += 3) {
                    arr[k] *= -1;   //x 翻转 处理左右手坐标系不一致问题
                }
            }
        }

        // private reverseVec3All(accIdx: number, arr: AccComponentType) {
        //     if (!this._accIdMarkMap.get(accIdx)) {
        //         this._accIdMarkMap.set(accIdx, true);
        //         //修正坐标系
        //         for (let k = 0, len2 = arr.length; k < len2; k += 3) {
        //             //x 翻转 处理左右手坐标系不一致问题
        //             arr[k] *= -1;
        //             arr[k + 1] *= -1;
        //             arr[k + 2] *= -1;
        //         }
        //     }
        // }

        /**
         * 顶点变形处理
         * @param morphWeights 
         * @param morphTargets 
         * @param aKey 
         * @param arr 
         * @param reverseVec3X 
         * @returns 
         */
        private morphVertex(morphWeights: number[] | undefined, morphTargets: Attribute[] | undefined, aKey: string, arr: AccComponentType, reverseVec3X = false): boolean {
            if (!morphWeights || !morphTargets) return false;
            //顶点变形操作
            for (let k = 0, len2 = morphTargets.length; k < len2; k++) {
                const morphTarget = morphTargets[k];
                const morphWeight = morphWeights[k];
                const _accIdx = morphTarget[aKey];
                if (morphWeight == null || _accIdx == null) break;
                const _arr = this.getTypeArrByAcc(_accIdx);
                if (reverseVec3X) {
                    this.reverseVec3X(_accIdx, _arr);
                }
                //变形融合
                for (let l = 0, len3 = _arr.length; l < len3; l++) {
                    arr[l] += _arr[l] * morphWeight;
                }
            }

            return true;
        }

        /**
         * 获取指定索引的静态网格节点
         * @param index 索引值
         */
        private getMesh(index: number): Nullable<Mesh> {
            const d = this._data;
            const mesh = d.meshes[index];
            const name = mesh.name ?? `gltf_mesh_${index}`;
            const resMesh = new Mesh();
            resMesh.name = name;
            const mData = resMesh.data = new MeshData();
            //解析
            const primArr = mesh.primitives;
            const morphWeights = mesh.weights;
            //遍历 primitive
            for (let i = 0, len = primArr.length; i < len; i++) {
                const p = primArr[i];
                const mP = new MeshPrimitive();
                // mP.frontFace = App.webgl2.CW;
                const attr = p.attributes;
                const keys = Object.keys(attr);
                const lastVertexCount = mData.posArr ? mData.posArr.length / 3 : 0;
                const morphTargets = p.targets;
                let hasPosMorphed = false;
                for (let j = 0, len1 = keys.length; j < len1; j++) {
                    const aKey = keys[j];
                    const attrLayout: AttributeLayout = (AttributeLayout as any)[aKey];
                    if (attrLayout == null) continue;
                    //accessor
                    const accIdx = attr[aKey];
                    const acc = d.accessors[accIdx];
                    const arr = this.getTypeArrByAcc(accIdx);
                    const isPostion = aKey == "POSITION";
                    //翻转x
                    if (isPostion) { this.reverseVec3X(accIdx, arr); }
                    //变形处理
                    const hasMorphed = this.morphVertex(morphWeights, morphTargets, aKey, arr, isPostion);
                    if (isPostion) { hasPosMorphed = hasMorphed; }

                    switch (attrLayout) {
                        case AttributeLayout.POSITION:
                            mP.start = mData.posArr?.length ?? 0;
                            mP.length = arr.length;
                            mData.posArr = this.tryMergeTypeArr<Float32Array>(mData.posArr, arr, Float32Array);
                            break;
                        case AttributeLayout.TEXCOORD_0:
                            mData.uvArr = this.tryMergeTypeArr<Float32Array>(mData.uvArr, arr, Float32Array);
                            break;
                        case AttributeLayout.TEXCOORD_1:
                            mData.uvArr1 = this.tryMergeTypeArr<Float32Array>(mData.uvArr1, arr, Float32Array);
                            break;
                        case AttributeLayout.TEXCOORD_2:
                            mData.uvArr2 = this.tryMergeTypeArr<Float32Array>(mData.uvArr2, arr, Float32Array);
                            break;
                        case AttributeLayout.NORMAL:
                            // this.reverseVec3All(accIdx, arr);
                            mData.normalArr = this.tryMergeTypeArr<Float32Array>(mData.normalArr, arr, Float32Array);
                            break;
                        case AttributeLayout.TANGENT:
                            mData.tangentArr = this.tryMergeTypeArr<Float32Array>(mData.tangentArr, arr, Float32Array);
                            break;
                        // case AttributeLayout.COLOR_0: mData.colorArr = new Float32Array(bin.buffer, offset, byteLen); break;
                        // case AttributeLayout.TEXCOORD_1: mData.uv1Arr = new Float32Array(bin.buffer, offset, byteLen); break;
                        // case AttributeLayout.TANGENT: mData.tangentArr = new Float32Array(bin.buffer, offset, byteLen); break;
                        default:
                    }

                    // bufferView.target ，不用处理,mData.triIndexArr 中有数据就走 ELEMENT_ARRAY_BUFFER

                    //accessor.max | min
                    if (attrLayout == AttributeLayout.POSITION) {
                        if (acc.min && acc.max) {
                            mData.posMin.x = Math.min(i == 0 ? Number.POSITIVE_INFINITY : mData.posMin.x, acc.min[0]);
                            mData.posMin.y = Math.min(i == 0 ? Number.POSITIVE_INFINITY : mData.posMin.y, acc.min[1]);
                            mData.posMin.z = Math.min(i == 0 ? Number.POSITIVE_INFINITY : mData.posMin.z, acc.min[2]);
                            mData.posMax.x = Math.max(i == 0 ? Number.NEGATIVE_INFINITY : mData.posMax.x, acc.max[0]);
                            mData.posMax.y = Math.max(i == 0 ? Number.NEGATIVE_INFINITY : mData.posMax.y, acc.max[1]);
                            mData.posMax.z = Math.max(i == 0 ? Number.NEGATIVE_INFINITY : mData.posMax.z, acc.max[2]);
                        }
                    }
                }
                if (hasPosMorphed) {
                    //坐标有变形需要修复normal
                    //....
                }
                //primitives.indices
                if (p.indices != null) {
                    const arr = this.getTypeArrByAcc(p.indices) as any;
                    //三角形顶点索引
                    const needTriIdxOffset = i != 0;
                    if (needTriIdxOffset) {
                        for (let j = 0, len1 = arr.length as number; j < len1; j++) {
                            arr[j] += lastVertexCount;
                        }
                    }

                    mP.start = mData.triIndexArr?.length ?? 0;
                    mP.length = arr.length;
                    const burClass = arr.constructor;
                    mData.triIndexArr = this.tryMergeTypeArr<any>(mData.triIndexArr, arr, burClass);

                    //buffer Start
                    const triIdxBufByteSize = this.getAccComponentSize(this._accInfoCacheMap[p.indices].compType);
                    mP.bufferStart = mP.start * triIdxBufByteSize;
                } else {
                    //不走 顶点索引模式
                    mP.start /= 3;
                    mP.length /= 3;
                    mP.bufferStart = mP.start * 4;
                }

                //primitives.material
                mP.material = i;
                //primitives.mode
                mP.mode = this.getMeshPGLMode(p.mode ?? MeshPrimitivesMode.TRIANGLES);
                // mP.mode = this.getMeshPGLMode(p.mode ?? MeshPrimitivesMode.POINTS);
                //
                resMesh.primitives.push(mP);
            }

            //将来支持动画时处理 weights
            //applay
            // resMesh.data.calcPosBoundary();  //gltf 有带 min max 包围盒 ，不需要算了
            resMesh.apply();
            return resMesh;
        }

        /**
         * 获取 MeshPrimitivesMode 对应webgl mode常量
         * @param mode MeshPrimitivesMode
         * @returns webgl mode常量
         */
        private getMeshPGLMode(mode: MeshPrimitivesMode) {
            const gl = App.webgl2;
            const glKey = MeshPrimitivesMode[mode];
            return (gl as any)[glKey];
        }

        /**
         * 尝试获取 合并 两个类型化数组
         * @param arrA 类型化数组对象A
         * @param arrB 类型化数组对象A
         * @param c 类型化数组构造器
         * @returns 合并后的类型化数组对象
         */
        private tryMergeTypeArr<T extends AccComponentType>(arrA: Nullable<AccComponentType>, arrB: Nullable<AccComponentType>, c: AccComponentTypeC): Nullable<T> {
            if (!arrA && !arrB) return null;
            if (!arrA) return arrB as any;
            if (!arrB) return arrA as any;
            let result: Nullable<T>;
            //检查是否是连续的
            if (arrA.buffer == arrB.buffer && arrA.byteOffset + arrA.byteLength == arrB.byteOffset) {
                //是连续不用新开内存,只要 拓展一下范围
                result = new c(arrA.buffer, arrA.byteOffset, arrA.length + arrB.length) as T;
            } else {
                //不是连续的,需要新开内存，然后将两 类型化数组，拷贝放入
                result = new c(arrA.length + arrB.length) as T;
                result.set(arrA);
                result.set(arrB, arrA.length);
            }
            return result;
        }

        /**
         * 通过Acc 获取类型数组对象
         * @param accIndex Acc 索引
         * @returns 型数组对象
         */
        private getTypeArrByAcc(accIndex: number) {
            const d = this._data;
            //获取bin 文件url
            let accInfo = this._accInfoCacheMap[accIndex];
            if (!accInfo) {
                //acc
                const acc = d.accessors[accIndex];
                //bufView
                const bufView = d.bufferViews[acc.bufferView ?? 0];
                const bufferIdx = bufView.buffer;
                //buffer
                // const buffer = d.buffers[bufView.buffer];
                // const fUrl = `${this._gltfPath}${buffer.uri}`;
                const eleSize = this.getAccTypeEleSize(acc.type);
                const unitByteSize = this.getAccComponentSize(acc.componentType) * eleSize;
                const isRealStrideMode = bufView.byteStride != null && bufView.byteStride > unitByteSize;
                const compType: number = acc.componentType;
                let byteOffset: number = 0;
                let byteLen: number = 0;
                if (!isRealStrideMode) {
                    //不用考虑 bufferView.byteStride 交错数据
                    byteOffset = (acc.byteOffset ?? 0) + (bufView.byteOffset ?? 0);
                    byteLen = acc.count * eleSize;
                } else {
                    //
                }
                this._accInfoCacheMap[accIndex] = accInfo = { bufferIdx, compType, byteOffset, byteLen };
            }

            //bin 获取
            const resKey = this._binResKeys[accInfo.bufferIdx];
            const bin = AssetManager.GetAsset(resKey) as BinaryData;
            const typeArrConstructor = this.getAccComponentArrTypeC(accInfo.compType);
            //生成数组
            const arr = new typeArrConstructor(bin.buffer, accInfo.byteOffset, accInfo.byteLen);
            return arr;
        }

        /**
         * 获取 AccType 对应元素数量
         * @param type AccType
         * @returns 元素数量
         */
        private getAccTypeEleSize(type: string) {
            switch (type) {
                case "SCALAR": return 1;
                case "VEC2": return 2;
                case "VEC3": return 3;
                case "VEC4": return 4;
                case "MAT2": return 4;
                case "MAT3": return 9;
                case "MAT4": return 16;
                default: return 0;
            }
        }

        /**
         * 获取 AccComponent 对应类型化数组构造器
         * @param componentType AccComponent
         * @returns 类型化数组 type
         */
        private getAccComponentArrTypeC(componentType: AccComponentTEnum): AccComponentTypeC {
            switch (componentType) {
                case AccComponentTEnum.BYTE: return Int8Array;
                case AccComponentTEnum.UNSIGNED_BYTE: return Uint8Array;
                case AccComponentTEnum.SHORT: return Int16Array;
                case AccComponentTEnum.UNSIGNED_SHORT: return Uint16Array;
                case AccComponentTEnum.UNSIGNED_INT: return Uint32Array;
                case AccComponentTEnum.FLOAT: return Float32Array;
                default: return Float32Array;
            }
        }

        /**
         * 获取 AccComponent 对应字节长度
         * @param componentType AccComponent
         * @returns 字节长度
         */
        private getAccComponentSize(componentType: AccComponentTEnum) {
            switch (componentType) {
                case AccComponentTEnum.BYTE: return 1;
                case AccComponentTEnum.UNSIGNED_BYTE: return 1;
                case AccComponentTEnum.SHORT: return 2;
                case AccComponentTEnum.UNSIGNED_SHORT: return 2;
                case AccComponentTEnum.UNSIGNED_INT: return 4;
                case AccComponentTEnum.FLOAT: return 4;
                default: return 0;
            }
        }
    }
}